#include "ByteArray.hpp"

using namespace std;



Network::ByteArray::ByteArray()
 : _capacity(0), _size(0), _data(NULL)
{
	
}



Network::ByteArray::ByteArray(int n)
 : _capacity(n), _size(n)
{
	if(n == 0)
	{
		_data = NULL;
	}
	else
	{
		_data = malloc(n);
		
		if(_data == NULL)
			throw std::bad_alloc();
	}
}



Network::ByteArray::ByteArray(int n, char value)
 : _capacity(n), _size(n)
{
	if(n == 0)
	{
		_data = NULL;
	}
	else
	{
		_data = malloc(n);
		
		if(_data == NULL)
			throw std::bad_alloc();
		
		memset(_data, value, n);
	}
}



Network::ByteArray::ByteArray(char data)
 : _capacity(1), _size(1)
{
	_data = malloc(1);
	
	if(_data == NULL)
		throw std::bad_alloc();
	
	static_cast<char*>(_data)[0] = data;
}



Network::ByteArray::ByteArray(const void* data, int len)
 : _capacity(len), _size(len)
{
	if(len == 0)
	{
		_data = NULL;
	}
	else
	{
		_data = malloc(len);
		
		if(_data == NULL)
			throw std::bad_alloc();
		
		memcpy(_data, data, len);
	}
}



Network::ByteArray::ByteArray(const char* data)
{
	_size = strlen(data);
	_capacity = _size;
	
	
	if(_size == 0)
	{
		_data = NULL;
	}
	else
	{
		_data = malloc(_size);
		
		if(_data == NULL)
			throw std::bad_alloc();
		
		memcpy(_data, data, _size);
	}
}



Network::ByteArray::ByteArray(const std::string& data)
{
	_size = data.size();
	_capacity = _size;
	
	if(_size == 0)
	{
		_data = NULL;
	}
	else
	{
		_data = malloc(_size);
		
		if(_data == NULL)
			throw std::bad_alloc();
		
		memcpy(_data, data.c_str(), _size);
	}
}



Network::ByteArray::ByteArray(const std::vector<char>& data)
{
	_size = data.size();
	_capacity = _size;
	
	if(_size == 0)
	{
		_data = NULL;
	}
	else
	{
		_data = malloc(_size);
		
		if(_data == NULL)
			throw std::bad_alloc();
		
		memcpy(_data, static_cast<const void*>(&(data[0])), _size);
	}
}



Network::ByteArray::ByteArray(const Network::ByteArray& other)
{
	_size = other._size;
	_capacity = other._size;
	
	if(_size == 0)
	{
		_data = NULL;
	}
	else
	{
		_data = malloc(_size);
		
		if(_data == NULL)
			throw std::bad_alloc();
		
		memcpy(_data, other._data, _size);
	}
}



Network::ByteArray::~ByteArray()
{
	if(_data != NULL)
		free(_data);
}



void Network::ByteArray::resize(int size) throw(ArgumentException)
{
	if(size < 0)
		throw ArgumentException("size argument must be strictly positive");
	
	if(size > _capacity || size <= (_capacity<<1))
	{
		size_t capacity = Network::ByteArray::pow2ge(size);
		void* data = realloc(_data, capacity);
		
		if(capacity > 0 && data == NULL)
			throw std::bad_alloc();
		
		_capacity = capacity;
		_data = data;
	}
	
	_size = size;
}
		


void Network::ByteArray::resize(int size, char value) throw(ArgumentException)
{
	if(size < 0)
		throw ArgumentException("size argument must be strictly positive");
	
	if(size > _capacity || size <= (_capacity<<1))
	{
		size_t capacity = pow2ge(size);
		void* data = realloc(_data, capacity);
		
		if(data == NULL)
			throw std::bad_alloc();
		
		_capacity = capacity;
		_data = data;
	}
	
	if(size > _size)
		memset(static_cast<void*>(static_cast<char*>(_data)+_size), value, size-_size);
	
	_size = size;
}



int Network::ByteArray::count() const
{
	return _size;
}



int Network::ByteArray::count(char byte, int from) const
{
	if(from < 0)
		 from = 0;
	
	int nb = 0;
	
	for(int i=from ; i<_size ; ++i)
		if(static_cast<char*>(_data)[i] == byte)
			nb++;
	
	return nb;
}



int Network::ByteArray::count(const Network::ByteArray& ba, int from) const
{
	if(from < 0)
		 from = 0;
	
	int nb = 0;
	int cur = from;
	
	while(cur <= static_cast<int>(_size-ba._size))
	{
		cur = find(ba[0], cur);
	
		if(cur == -1)
			return nb;
		
		if(memcmp(_data, ba._data, ba._size) == 0)
		{
			nb++;
			cur += ba.size();
		}
		else
		{
			cur++;
		}
	}
	
	return nb;
}



int Network::ByteArray::find(char byte, int from) const
{
	if(from < 0)
		 from = 0;

	char* dataInit = static_cast<char*>(_data) + from;
	const char* dataEnd = static_cast<char*>(_data) + _size;
	
	while(dataInit < dataEnd)
	{
		if(*dataInit == byte)
			return dataInit-static_cast<char*>(_data);
		
		dataInit++;
	}
	
	return -1;
}



int Network::ByteArray::find(const Network::ByteArray& ba, int from) const
{
	if(from < 0)
		 from = 0;
	
	if(ba._size <= 0)
		return count();
	
	int cur = from;
	
	while(cur <= static_cast<int>(_size-ba._size))
	{
		cur = find(ba[0], cur);
		
		if(cur == -1)
			return -1;
		
		void* data = static_cast<void*>(static_cast<char*>(_data)+cur);
		
		if(memcmp(data, ba._data, ba._size) == 0)
			return cur;
		else
			cur++;
	}
	
	return -1;
}



int Network::ByteArray::size() const
{
	return _size;
}



int Network::ByteArray::capacity() const
{
	return _capacity;
}



bool Network::ByteArray::empty() const
{
	return (_size == 0);
}

#include <cstdio>

Network::ByteArray Network::ByteArray::left(int length) const
{
	if(length < 0)
		return right(-length);
	
	if(length > _size)
	{
		ByteArray ba(_size);
		memcpy(ba._data, _data, _size);
		return ba;
	}
	else
	{
		ByteArray ba(length);
		memcpy(ba._data, _data, length);
		return ba;
	}
}



Network::ByteArray Network::ByteArray::right(int length) const
{
	if(length < 0)
		return left(-length);
	
	if(length > _size)
	{
		ByteArray ba(_size);
		const void* data = _data;
		memcpy(ba._data, data, _size);
		return ba;
	}
	else
	{
		ByteArray ba(length);
		const void* data = static_cast<const void*>(static_cast<const char*>(_data)+_size-length);
		memcpy(ba._data, data, length);
		return ba;
	}
}



Network::ByteArray Network::ByteArray::mid(int from, int length) const
{
	if(from < 0 || from >= _size)
		
	
	if(length < 0)
	{
		if(from+length >= 0)
			return mid(from+length, -length);
		else
			return mid(0, from);
	}
	
	if(from+length > _size)
	{
		ByteArray ba(_size-from);
		const void* data = static_cast<const void*>(static_cast<const char*>(_data)+from);
		memcpy(ba._data, data, _size-from);
		return ba;
	}
	else
	{
		ByteArray ba(length);
		const void* data = static_cast<const void*>(static_cast<const char*>(_data)+from);
		memcpy(ba._data, data, length);
		return ba;
	}
}



void Network::ByteArray::fromCString(const char* data)
{
	int len = strlen(data);
	resize(len);
	memcpy(_data, static_cast<const void*>(data), len);
}



void Network::ByteArray::fromString(const string& data)
{
	int len = data.size();
	resize(len);
	memcpy(_data, static_cast<const void*>(data.c_str()), len);
}



void Network::ByteArray::fromVector(const vector<char>& data)
{
	int len = data.size();
	resize(len);
	memcpy(_data, static_cast<const char*>(&(data[0])), len);
}



string Network::ByteArray::toString() const
{
	if(_size <= 0)
		return string();
	
	return string(static_cast<char*>(_data), _size);
}



vector<char> Network::ByteArray::toVector() const
{
	if(_size <= 0)
		return vector<char>();
	
	return vector<char>(static_cast<char*>(_data), static_cast<char*>(_data)+_size);
}



bool Network::ByteArray::operator==(const ByteArray& other) const
{
	if(_size == other._size)
	{
		if(_size == 0)
			return true;
		
		if(memcmp(_data, other._data, _size) == 0)
			return true;
	}
	
	return false;
}



bool Network::ByteArray::operator!=(const ByteArray& other) const
{
	if(_size == other._size)
	{
		if(_size == 0)
			return false;
		
		if(memcmp(_data, other._data, _size) == 0)
			return false;
	}
	
	return true;
}



Network::ByteArray& Network::ByteArray::operator+=(const Network::ByteArray& other)
{
	int thisSize = _size;
	int otherSize = other._size;
	
	resize(thisSize + otherSize);
	
	memcpy(static_cast<void*>(static_cast<char*>(_data)+thisSize), other._data, otherSize);
	
	return *this;
}



Network::ByteArray& Network::ByteArray::operator=(const Network::ByteArray& other)
{
	if(&other != this)
	{
		int len = other._size;
		
		resize(len);
		memcpy(_data, other._data, len);
	}
	
	return *this;
}



char& Network::ByteArray::operator[](int index)
{
	//assert(index >= 0);
	//assert(static_cast<size_t>(index) < _size);
	return static_cast<char*>(_data)[index];
}



const char& Network::ByteArray::operator[](int index) const
{
	//assert(index >= 0);
	//assert(static_cast<size_t>(index) < _size);
	return static_cast<char*>(_data)[index];
}



// It must be implemented inside "Network" block
namespace Network
{
	Network::ByteArray operator+(const Network::ByteArray& ba1, const Network::ByteArray& ba2)
	{
		Network::ByteArray ba(ba1._size + ba2._size);
	
		memcpy(ba._data, ba1._data, ba1._size);
		memcpy(static_cast<void*>(static_cast<char*>(ba._data)+ba1._size), ba2._data, ba2._size);
	
		return ba;
	}
}



unsigned int Network::ByteArray::pow2ge(unsigned int n)
{
	unsigned int m = n & (n-1);
	
	if(m == 0)
		return n;
	
	do
	{
		n = m & (m-1);
		
		if(n == 0)
			return m<<1;
		
		m = n & (n-1);
	}
	while(m != 0);
	
	return n<<1;
}



